﻿using Abp.Application.Services;
using Abp.Application.Services.Dto;
using Abp.Domain.Entities;
using Abp.Domain.Repositories;
using Abp.Events.Bus;
using Abp.Extensions;
using Abp.IdentityFramework;
using Abp.Linq;
using Abp.MultiTenancy;
using Abp.Runtime.Session;
using AbpProjectTemplate.Application.Filters;
using AbpProjectTemplate.Domain.Users;
using AbpProjectTemplate.Domain;
using Microsoft.AspNetCore.Identity;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading.Tasks;
using System;
using Microsoft.EntityFrameworkCore;
using Abp.Authorization;
using Abp;
using Abp.Localization;
using Abp.Auditing;
using AbpProjectTemplate.Extensions;
using AbpProjectTemplate.Utilities.Theading;
using AbpProjectTemplate.Components.Auditing;

namespace AbpProjectTemplate.Application
{
    [DisableAuditing]
    public abstract class AsyncCrudApplicationService<TEntity, TPrimaryKey, TEntityDto, TEntityListItem, TGetPagedListInput, TCreateInput, TUpdateInput, TGetInput, TDeleteInput>
        : CrudAppServiceBase<TEntity, TEntityDto, TPrimaryKey, TGetPagedListInput, TCreateInput, TUpdateInput>
        , ICrudApplicationService<TEntityDto, TPrimaryKey, TEntityListItem, TGetPagedListInput, TCreateInput, TUpdateInput, TGetInput, TDeleteInput>
           where TEntity : class, IEntity<TPrimaryKey>
           where TEntityDto : IEntityDto<TPrimaryKey>
           where TEntityListItem : IEntityDto<TPrimaryKey>
           where TUpdateInput : IEntityDto<TPrimaryKey>
           where TGetInput : IEntityDto<TPrimaryKey>
           where TDeleteInput : IEntityDto<TPrimaryKey>
    {
        public IAsyncQueryableExecuter AsyncQueryableExecuter { get; set; }
        public IEventBus EventBus { get; set; }
        public IUserCache UserCache { get; set; }
        public ITenantCache TenantCache { get; set; }
        /// <summary>
        /// 并发控制器
        /// </summary>
        public SemaphoreSlimManager SemaphoreSlimManager { get; set; }
        public AsyncCrudApplicationService(IRepository<TEntity, TPrimaryKey> repository, string managerPermisson = null)
            : base(repository)
        {
            this.CreatePermissionName = managerPermisson;
            this.UpdatePermissionName = managerPermisson;
            this.GetPermissionName = managerPermisson;
            this.GetAllPermissionName = managerPermisson;
            this.DeletePermissionName = managerPermisson;

            this.AsyncQueryableExecuter = NullAsyncQueryableExecuter.Instance;
            this.EventBus = NullEventBus.Instance;
        }
        /// <summary>
        /// 当前登录用户ID
        /// </summary>
        protected long CurrentUserId
        {
            get
            {
                if (!this.AbpSession.UserId.HasValue)
                    throw new AbpAuthorizationException(this.LocalizationManager.GetString(AbpConsts.LocalizationSourceName, "CurrentUserDidNotLoginToTheApplication"));
                return this.AbpSession.UserId.Value;
            }
        }
        /// <summary>
        /// 获取当前登录用户信息
        /// </summary>
        protected virtual async Task<TenantCacheItem> GetCurrentTenantAsync()
        {
            var item = this.TenantCache.GetOrNull(this.AbpSession.GetTenantId());
            if (item == null)
            {
                throw new AppUserFriendlyException($"不存在的租户：{this.AbpSession.GetTenantId()}。");
            }
            return await Task.FromResult(item);
        }
        /// <summary>
        /// 获取当前登录用户信息
        /// </summary>
        protected virtual async Task<User> GetCurrentUserAsync()
        {
            var item = await this.UserCache.GetAsync(this.CurrentUserId);
            if (item == null)
            {
                throw new AppUserFriendlyException($"不存在的用户：{this.CurrentUserId}。");
            }
            return item;
        }
        /// <summary>
        /// identityResult.CheckErrors
        /// </summary>
        protected virtual void CheckIdentityErrors(IdentityResult identityResult)
        {
            identityResult.CheckErrors(this.LocalizationManager);
        }
        /// <summary>
        /// id获取实体数据 如果不存在则抛出异常
        /// </summary>
        protected virtual Task<TEntity> GetEntityByIdAsync(TPrimaryKey id)
        {
            return this.Repository.GetAsync(id);
        }
        /// <summary>
        /// 过滤数据
        /// </summary>
        protected override IQueryable<TEntity> CreateFilteredQuery(TGetPagedListInput input)
        {
            return base.CreateFilteredQuery(input);
        }
        /// <summary>
        /// 应用排序
        /// </summary>
        protected override IQueryable<TEntity> ApplySorting(IQueryable<TEntity> query, TGetPagedListInput input)
        {
            //Try to sort query if available
            var sortInput = input as ISortedResultRequest;
            if (sortInput != null)
            {
                if (!sortInput.Sorting.IsNullOrWhiteSpace())
                {
                    return query.OrderBy(sortInput.Sorting);
                }
            }

            if (typeof(IHasSort).IsAssignableFrom(typeof(TEntity)))
            {
                return query.OrderByDescending(t => ((IHasSort)t).Sort);
            }

            //IQueryable.Task requires sorting, so we should sort if Take will be used.
            if (input is PagedRequestFilter)
            {
                var filter = input as PagedRequestFilter;
                filter.SetDefaultSortIfNot("Id");
                return query.SortBy(filter);
            }

            //No sorting
            return query;
        }
        /// <summary>
        /// 应用分页
        /// </summary>
        protected override IQueryable<TEntity> ApplyPaging(IQueryable<TEntity> query, TGetPagedListInput input)
        {
            var pageModel = input as PagedRequestFilter;
            if (pageModel != null)
            {
                return query.PageBy(pageModel);
            }
            return query;
        }
        /// <summary>
        /// 映射创建实体
        /// </summary>
        protected override TEntity MapToEntity(TCreateInput createInput)
        {
            var entity = base.MapToEntity(createInput);
            entity.Id = default(TPrimaryKey);
            return entity;
        }
        /// <summary>
        /// 映射数据到更新实体
        /// </summary>
        protected override void MapToEntity(TUpdateInput updateInput, TEntity entity)
        {
            var id = entity.Id;
            base.MapToEntity(updateInput, entity);
            entity.Id = id;
        }
        /// <summary>
        /// 创建分页数据
        /// </summary>
        protected virtual async Task<PagedResultDto<T>> CreatePagedResultAsync<T>(IQueryable<T> query, PagedRequestFilter filter)
        {
            var totalCcount = await this.AsyncQueryableExecuter.CountAsync(query);
            var items = await query.PageAndSortBy(filter).ToListAsync();

            return new PagedResultDto<T>(totalCcount, items);
        }
        /// <summary>
        /// 创建分页数据创建并返回指定的Result对像
        /// </summary>
        protected virtual async Task<TResult> CreatePagedResultAsync<T, TResult>(IQueryable<T> query, PagedRequestFilter filter)
            where TResult : PagedResultDto<T>, new()
        {
            var totalCcount = await query.CountAsync();
            var items = await query.PageAndSortBy(filter).ToListAsync();

            var result = new TResult
            {
                Items = items,
                TotalCount = totalCcount
            };
            return result;
        }
        /// <summary>
        /// 创建分页数据返回结果，并自动从TEntity映射到TEntityDto（先返回数据再映射）
        /// </summary>
        protected virtual async Task<TResult> CreatePagedResultMapAfterAsync<T, TDto, TResult>(
            IQueryable<T> query,
            PagedRequestFilter filter)
            where TResult : PagedResultDto<TDto>, new()
        {
            var totalCcount = await query.CountAsync();
            var items = await query.PageAndSortBy(filter).ToListAsync();

            var result = new TResult { Items = this.ObjectMapper.Map<List<TDto>>(items), TotalCount = totalCcount };
            return result;
        }

        /// <summary>
        /// 创建分页数据（先返回所有数据再映射）
        /// </summary>
        protected virtual async Task<PagedResultDto<TDto>> CreatePagedResultMapAfterAsync<T, TDto>(
            IQueryable<T> query,
            PagedRequestFilter filter)
        {
            return await this.CreatePagedResultMapAfterAsync<T, TDto, PagedResultDto<TDto>>(query, filter);
        }
        #region 公共服务

        /// <summary>
        /// 创建
        /// </summary>
        [AppAudited("添加")]
        public virtual async Task<TEntityDto> Create(TCreateInput input)
        {
            this.CheckCreatePermission();

            var entity = this.MapToEntity(input);
            entity.Id = default(TPrimaryKey);
            await this.Repository.InsertAsync(entity);
            await this.CurrentUnitOfWork.SaveChangesAsync();

            return this.MapToEntityDto(entity);
        }
        /// <summary>
        /// 获取详细
        /// </summary>
        public virtual async Task<TEntityDto> Get(TGetInput input)
        {
            this.CheckGetPermission();

            var entity = await this.GetEntityByIdAsync(input.Id);
            return this.MapToEntityDto(entity);
        }
        /// <summary>
        /// 查询分页列表
        /// </summary>
        public virtual async Task<PagedResultDto<TEntityListItem>> GetPagedList(TGetPagedListInput input)
        {
            this.CheckGetAllPermission();

            var query = this.CreateFilteredQuery(input);

            var totalCount = await this.AsyncQueryableExecuter.CountAsync(query);

            query = this.ApplySorting(query, input);
            query = this.ApplyPaging(query, input);
            var queryResult = query;//.ProjectTo<TListEntityItem>();
            var result = await this.AsyncQueryableExecuter.ToListAsync(queryResult);

            return new PagedResultDto<TEntityListItem>(
                totalCount,
                this.ObjectMapper.Map<IReadOnlyList<TEntityListItem>>(result)
            );
        }
        /// <summary>
        /// 更新
        /// </summary>
        [AppAudited("修改")]
        public virtual async Task<TEntityDto> Update(TUpdateInput input)
        {
            this.CheckUpdatePermission();

            var entity = await this.GetEntityByIdAsync(input.Id);

            this.MapToEntity(input, entity);

            await this.CurrentUnitOfWork.SaveChangesAsync();

            return this.MapToEntityDto(entity);
        }
        /// <summary>
        /// 删除
        /// </summary>
        [AppAudited("删除")]
        public virtual async Task Delete(TDeleteInput input)
        {
            this.CheckDeletePermission();

            await this.Repository.DeleteAsync(input.Id);
        }
        /// <summary>
        /// 批量删除
        /// </summary>
        protected virtual async Task Deletes(IEnumerable<TPrimaryKey> ids)
        {
            this.CheckDeletePermission();

            await this.Repository.DeleteAsync(t => ids.Contains(t.Id));
        }
        #endregion
    }

    public class AsyncCrudApplicationService<TEntity, TPrimaryKey, TEntityDto, TEntityListItem, TGetPagedListInput, TCreateInput, TUpdateInput>
      : AsyncCrudApplicationService<TEntity, TPrimaryKey, TEntityDto, TEntityListItem, TGetPagedListInput, TCreateInput, TUpdateInput, EntityDto<TPrimaryKey>, EntityDto<TPrimaryKey>>
      where TEntity : class, IEntity<TPrimaryKey>
      where TEntityDto : IEntityDto<TPrimaryKey>
      where TEntityListItem : IEntityDto<TPrimaryKey>
      where TUpdateInput : IEntityDto<TPrimaryKey>
    {
        public AsyncCrudApplicationService(IRepository<TEntity, TPrimaryKey> repository, string managerPermisson = null) : base(repository, managerPermisson) { }
    }

    public class AsyncCrudApplicationService<TEntity, TPrimaryKey, TEntityDto, TEntityListItem, TGetPagedListInput, TEditDto>
      : AsyncCrudApplicationService<TEntity, TPrimaryKey, TEntityDto, TEntityListItem, TGetPagedListInput, TEditDto, TEditDto, EntityDto<TPrimaryKey>, EntityDto<TPrimaryKey>>
      where TEntity : class, IEntity<TPrimaryKey>
      where TEntityDto : IEntityDto<TPrimaryKey>
      where TEntityListItem : IEntityDto<TPrimaryKey>
      where TEditDto : IEntityDto<TPrimaryKey>
    {
        public AsyncCrudApplicationService(IRepository<TEntity, TPrimaryKey> repository, string managerPermisson = null) : base(repository, managerPermisson) { }
    }

    public class AsyncCrudApplicationService<TEntity, TEntityDto, TEntityListItem, TGetPagedListInput, TEditDto>
      : AsyncCrudApplicationService<TEntity, int, TEntityDto, TEntityListItem, TGetPagedListInput, TEditDto, TEditDto, EntityDto<int>, EntityDto<int>>
      where TEntity : class, IEntity<int>
      where TEntityDto : IEntityDto<int>
      where TEntityListItem : IEntityDto<int>
      where TEditDto : IEntityDto<int>
    {
        public AsyncCrudApplicationService(IRepository<TEntity, int> repository, string managerPermisson = null) : base(repository, managerPermisson) { }
    }
}

